package com.codeckz.library_face.controller

import android.content.Context
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.SurfaceTexture
import android.graphics.YuvImage
import android.hardware.Camera
import android.os.Handler
import android.os.Looper
import android.util.SparseIntArray
import android.view.Surface
import android.view.TextureView
import android.view.TextureView.SurfaceTextureListener
import androidx.camera.view.PreviewView
import com.codeckz.library_face.utils.*
import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.*


/**
 *@packageName com.codeckz.library_face.controller
 *@author kzcai
 *@date 2022/1/18
 */
class Camera19Controller(private val context: Context) : ICameraController(),CoroutineScope by MainScope() {



    private var camera: Camera? = null

    private var previewView: TextureView? = null

    private val preferredWidth = 640
    private val preferredHeight = 360

    private var cameraId = 1
    private var parameters: Camera.Parameters? = null


    companion object {
        private val ORIENTATIONS = SparseIntArray()
        private val MAX_PREVIEW_SIZE = 2048

        init {
            ORIENTATIONS.append(Surface.ROTATION_0, 90)
            ORIENTATIONS.append(Surface.ROTATION_90, 0)
            ORIENTATIONS.append(Surface.ROTATION_180, 270)
            ORIENTATIONS.append(Surface.ROTATION_270, 180)
        }
    }


    private val listener: SurfaceTextureListener = object : SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, i: Int, i1: Int) {

            startCamera()

        }

        override fun onSurfaceTextureSizeChanged(surfaceTexture: SurfaceTexture, i: Int, i1: Int) {

        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
            resetCamera()
            return true
        }

        override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {}
    }


    override fun setPreviewView(view: PreviewView) {

    }

    override fun setPreviewView(view: TextureView) {
        previewView = view
        previewView?.surfaceTextureListener = listener
    }


    override fun startCamera() {
        if (camera == null) {
            val cameraInfo = Camera.CameraInfo()
            for (i in 0 until Camera.getNumberOfCameras()) {
                Camera.getCameraInfo(i, cameraInfo)
                if (cameraInfo.facing == cameraFacing) {
                    cameraId = i
                }
            }
            try {
                camera = Camera.open(cameraId)
                //                camera.unlock();
//                setCameraFacing(CAMERA_FACING_BACK);
            } catch (e: Exception) {
//                camera = Camera.open(0);
//                setCameraFacing(CAMERA_FACING_BACK);
                e.printStackTrace()
            }
        }
        if (parameters == null) {
            parameters = camera?.parameters
            parameters?.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)
            parameters?.setRotation(90) // TODO
            //            parameters.setPreviewSize(1280,720);
        }
        opPreviewSize(preferredWidth, preferredHeight)
        camera?.setPreviewTexture(previewView?.surfaceTexture)
        camera?.startPreview()
//        initAnalize()
        Handler(Looper.myLooper()!!).postDelayed({
            initAnalize()
        }, 0)

    }

    private fun initAnalize() {
        try {
            camera?.setPreviewCallback { data, camera -> //                    Log.e("onPreviewFrame", "onPreviewFrame");
                launch(Dispatchers.IO){
                    val size = camera.parameters.previewSize
                    val localYuvImage = YuvImage(data, ImageFormat.NV21, size.width, size.height, null)
                    //
                    val bitmap = localYuvImage.toBitmap()
                    val matrix = Matrix()
                    matrix.setRotate(
                        if (cameraFacing == FRONT_CAMERA) -90f else 90f
                    )
                    if (cameraFacing== FRONT_CAMERA){

                    }


                    val offset =
                        if (cameraFacing == ICameraController.FRONT_CAMERA) 20 else 0
                    val newBM = bitmap?.fitScreen(
                        context,
                        if (cameraFacing == FRONT_CAMERA) -90f else 90f
                    )

                    val cutBm = newBM?.cutRect(context, circleRect!!, offset)
                    bitmap?.recycle()
                    if (cutBm != null) {
                        if (filterFace == null) {
                            filterFace = AndroidFilterFace()
                        }
                        val result = filterFace!!.checkFace(
                            newBM,
                            circleRect!!.bitmapCheckRect(
                                context,
                                newBM,
                                offset
                            )
                        )
                        val data = cutBm.toByteArray()
                        withContext(Dispatchers.Main){
                            onImageAnalysisListener?.invoke(data, result)
                        }
                        newBM.recycle()
                        cutBm.recycle()

                    }
                }
            }

        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun opPreviewSize(width: Int, height: Int) {
        val optSize: Camera.Size?
        if (parameters != null && camera != null && width > 0) {
            optSize = getOptimalSize(width, height, camera?.parameters?.supportedPreviewSizes)
            parameters?.setPreviewSize(optSize?.width?:0, optSize?.height?:0)

            camera?.setDisplayOrientation(90);
            //            camera.stopPreview();
            try {
                camera?.parameters = parameters
            } catch (e: RuntimeException) {
                e.printStackTrace()
            }
            camera?.startPreview()
        }
    }

    private fun getOptimalSize(width: Int, height: Int, sizes: MutableList<Camera.Size>?): Camera.Size? {
        if (sizes==null){
            return null
        }
        val pictureSize = sizes[0]
        val candidates: MutableList<Camera.Size> = ArrayList()
        for (size in sizes) {
            if (size.width >= width && size.height >= height && size.width * height == size.height * width) {
                // 比例相同
                candidates.add(size)
            } else if (size.height >= width && size.width >= height && size.width * width == size.height * height) {
                // 反比例
                candidates.add(size)
            }
        }
        if (!candidates.isEmpty()) {
            return Collections.min(candidates, sizeComparator)
        }
        for (size in sizes) {
            if (size.width > width && size.height > height) {
                return size
            }
        }
        return pictureSize
    }

    private val sizeComparator: Comparator<Camera.Size> =
        Comparator<Camera.Size> { lhs, rhs -> java.lang.Long.signum(lhs.width.toLong() * lhs.height - rhs.width.toLong() * rhs.height) }


    override fun switchCamera() {
        if (cameraFacing == ICameraController.BACK_CAMERA) {
            switchCamera(FRONT_CAMERA)
        } else {
            switchCamera(BACK_CAMERA)
        }
    }

    override fun switchCamera(cameraFacing: Int) {
        this.cameraFacing = cameraFacing
        var cameraCount = 0
        val cameraInfo = Camera.CameraInfo()
        cameraCount = Camera.getNumberOfCameras()
        closeCamera()
        startCamera()
    }

    override fun release() {
        closeCamera()
    }


    private fun closeCamera() {
        if (camera != null) {
            camera = try {
                camera?.stopPreview()
                camera?.setPreviewCallback(null)
                camera?.lock()
                camera?.release()
                null
                //                cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
            } catch (e: Exception) {
                camera?.release()
                null
                //                cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
            }
        }
    }

    fun resetCamera() {
        closeCamera()
    }
}